package com.gitee.l0km.jsonvisitor.fastjson;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URI;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;

import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONReaderScanner;
import com.alibaba.fastjson.parser.JSONScanner;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.util.TypeUtils;

public class Fastjsons {
	/**
	 * 返回buffer中所有字节(position~limit),不改变buffer状态
	 * @param buffer
	 * @return buffer 为 null 时返回 null 
	 * @throws IOException 
	 */
	private static final byte[] getBytesInChannel(ReadableByteChannel channel) throws IOException{
		if(null == channel){
			return null;
		}
		AutoExpandingBuffer autoBuffer = new AutoExpandingBuffer();
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		while(channel.read(buffer)>0) {
			buffer.flip();
			autoBuffer.write(buffer);
			buffer.clear();
		}
		return autoBuffer.copy();
	}
	/**
	 * 返回buffer中所有字节(position~limit),不改变buffer状态
	 * @param buffer
	 * @return buffer 为 null 时返回 null 
	 */
	private static final byte[] getBytesInBuffer(ByteBuffer buffer){
		if(null == buffer){
			return null;
		}
		int pos = buffer.position();
		try{
			byte[] bytes = new byte[buffer.remaining()];
			buffer.get(bytes);
			return bytes;
		}finally{
			buffer.position(pos);
		}
	}
	/**
	 * 从{@link InputStream}读取字节数组<br>
	 * 当{@code in}为{@link FileInputStream}时，调用{@link #readBytes(FileInputStream)}(NIO方式)读取<br>
	 *  结束时会关闭{@link InputStream}
	 * @param in 为{@code null}返回{@code null}
	 * @return 读取的字节数组
	 * @throws IOException
	 * @throws IllegalArgumentException {@code in}为{@code null}
	 */
	private static final byte[] readBytes(InputStream in) throws IOException, IllegalArgumentException {
		if(null == in){
			return null;
		}
		try {
			int buffSize = Math.max(in.available(), 1024*8);
			byte[] temp = new byte[buffSize];
			ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize);
			int size = 0;
			while ((size = in.read(temp)) != -1) {
				out.write(temp, 0, size);
			}
			return  out.toByteArray();
		} finally {
			in.close();
		}
	}
	/**
	 * 返回buffer中所有char(position~limit),不改变buffer状态
	 * 
	 * @param buffer
	 * @return buffer 为 null 时返回 null
	 */
	private static final char[] getCharsInBuffer(CharBuffer buffer) {
		if (null == buffer) {
			return null;
		}
		int pos = buffer.position();
		try {
			char[] bytes = new char[buffer.remaining()];
			buffer.get(bytes);
			return bytes;
		} finally {
			buffer.position(pos);
		}
	}
	/**
	 * 返回buffer中所有char(position~limit),不改变buffer状态
	 * 
	 * @param reader
	 * @return buffer 为 null 时返回 null
	 * @throws IOException 
	 */
	private static final String getStringInReader(Reader reader) throws IOException {
		if (null == reader) {
			return null;
		}
		StringBuffer buffer = new StringBuffer();
		char[] buf = new char[1024];
		int size = 0;
		while ((size = reader.read(buf)) >0) {
			buffer.append(buffer,0,size);
		}
		return buffer.toString();
	}

	/**
	 * 将数据源转为String<br>
	 * 支持的类型：<br>
	 * {@link JSONLexer},{@link Path},{@link File},{@link InputStream},{@link ReadableByteChannel},
	 * {@link URL},{@link URI},
	 * {@link Reader},{@link String},{@link ByteBuffer},{@link CharBuffer},
	 * byte[],char[]<br>
	 * 否则抛出异常{@link IllegalArgumentException}
	 * 
	 * @param src
	 * @throws IllegalArgumentException 不支持的数据源类型
	 */
	public static String asInputString(Object src) throws IllegalArgumentException {
		try {
			if( src instanceof String) {
				return (String)src;
			}else if (src instanceof Path) {
				try (ReadableByteChannel channel = Files.newByteChannel(((Path) src))) {
					return asInputString(channel);
				}
			} else if (src instanceof File) {
				try (FileInputStream is = new FileInputStream(((File) src))) {
					return asInputString(is);
				}
			} else if (src instanceof InputStream) {
				return asInputString(readBytes((InputStream) src));
			} else if (src instanceof ReadableByteChannel) {
				return asInputString(getBytesInChannel((ReadableByteChannel) src));
			} else if (src instanceof URL) {
				try (InputStream is = ((URL) src).openStream()) {
					return asInputString(is);
				}
			} else if (src instanceof URI) {
				return asInputString(((URI) src).toURL());
			} else if (src instanceof Reader) {
				return asInputString(getStringInReader((Reader) src));
			} else if (src instanceof ByteBuffer) {
				return asInputString(getBytesInBuffer((ByteBuffer) src));
			} else if (src instanceof CharBuffer) {
				return asInputString(getCharsInBuffer((CharBuffer) src));
			} else if (src instanceof byte[]) {
				return asInputString(new String((byte[]) src));
			} else if (src instanceof char[]) {
				return asInputString(new String((char[]) src));
			} else if (null != src) {
				throw new IllegalArgumentException("UNSUPPORTED Source Type:" + src.getClass().getName());
			}
			return null;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 将数据源转为{@link JSONLexer}<br>
	 * @param src 输入的JSON数据,支持类型参见 {@link #asInputString(Object)}
	 * @throws IllegalArgumentException 不支持的数据源类型
	 * @see #asInputString(Object)
	 */
	public static JSONLexer createJSONLexer(Object src) throws IllegalArgumentException {
		if( src instanceof JSONLexer) {
			return (JSONLexer)src;
		}else if (src instanceof Reader) {
			return new JSONReaderScanner((Reader) src);
		} else if (src instanceof String) {
			return new JSONScanner((String) src);
		} 
		return createJSONLexer(asInputString(src));
	}
	/**
	 * 查找JSON中key指定名称的字段，如果找到解析并返回字段值，否则返回{@code null}
	 * @param src 输入的JSON数据,支持类型参见 {@link #asInputString(Object)}
	 * @param key
	 */
	public static Object seekField(Object src, String key) {
		DefaultJSONParser parser = new DefaultJSONParser(createJSONLexer(src));
		JSONScanner lexerBase = (JSONScanner)parser.getLexer();
		if (JSONToken.LBRACE == lexerBase.token()) {
			/**
			 * 参照 com.alibaba.fastjson.JSONPath.PropertySegment 中
			 * 对 JSONLexerBase.seekObjectToField方法的调用
			 */
			long propertyNameHash = TypeUtils.fnv1a_64(key);
			int matchStat = lexerBase.seekObjectToField(propertyNameHash, false);
			if (matchStat == JSONLexer.VALUE) {
				return parser.parse();
			}
		}
	    return null;
	}
	/**
	 * Helper class that wraps a byte[] so that it can expand and be reused. Users
	 * should call resizeIfNecessary to make sure the buffer has suitable capacity,
	 * and then use the array as needed. Note that the internal array will grow at a
	 * rate slightly faster than the requested capacity with the (untested)
	 * objective of avoiding expensive buffer allocations and copies.
	 */
	static class AutoExpandingBuffer {
		private byte[] array;
		private int pos;

		private final double growthCoefficient;

		public AutoExpandingBuffer(int initialCapacity, double growthCoefficient) {
			if (growthCoefficient < 1.0) {
				throw new IllegalArgumentException("Growth coefficient must be >= 1.0");
			}
			array = new byte[initialCapacity];
			this.growthCoefficient = growthCoefficient;
			this.pos = 0;
		}
		public AutoExpandingBuffer() {
			this(256, 2);
		}
		private void resizeIfNecessary(int size) {
			if (array.length < size) {
				byte[] newBuf = new byte[(int) (size * growthCoefficient)];
				System.arraycopy(array, 0, newBuf, 0, array.length);
				array = newBuf;
			}
		}

		public void write(byte[] toWrite, int off, int len) {
			resizeIfNecessary(pos + len);
			System.arraycopy(toWrite, off, array, pos, len);
			pos += len;
		}

		public void write(byte[] toWrite) {
			write(toWrite, 0, toWrite.length);
		}
		public void write(ByteBuffer buffer) {
			if(null != buffer){
				byte[] bytes = new byte[buffer.remaining()];
				buffer.get(bytes);
				write(bytes);
			}
		}

		public byte[] array() {
			return array;
		}

		public int pos() {
			return pos;
		}

		public void reset() {
			pos = 0;
		}

		public byte[] copy() {
			return Arrays.copyOf(array,pos);
		}
	}
}
